package demoMod.stsconsole.patches;

import demoMod.stsconsole.annotations.SpireInsertPatch;
import demoMod.stsconsole.exceptions.PatchingException;
import demoMod.stsconsole.patches.lib.ParamInfo;
import javassist.CannotCompileException;
import javassist.CtBehavior;
import javassist.CtMethod;

import java.util.List;

public class InsertPatchInfo extends ParameterPatchInfo {
    private final SpireInsertPatch info;
    private final List<LineNumberAndPatchType> locs;

    public InsertPatchInfo(SpireInsertPatch info, List<LineNumberAndPatchType> locs, CtBehavior ctMethodToPatch, CtMethod patchMethod) {
        super(ctMethodToPatch, patchMethod);
        canSpireReturn = true;
        canByRefParams = true;
        this.info = info;
        this.locs = locs;
    }

    @Override
    protected String debugMsg() {
        StringBuilder msgBuilder = new StringBuilder();
        for (LineNumberAndPatchType patchLoc : locs) {
            switch(patchLoc.patchType) {
                case ABSOLUTE:
                    msgBuilder.append("Adding Insert @ ").append(patchLoc.lineNumber).append("...\n");
                    break;
                case RELATIVE:
                    msgBuilder.append("Adding Insert @ r").append(patchLoc.relativeLineNumber).append(" (abs:").append(patchLoc.lineNumber).append(")...\n");
                    break;
            }
        }
        return msgBuilder.toString();
    }

    @Override
    public int patchOrdering() {
        return -2;
    }

    @Override
    protected ParamTransformer makeTransformer(ParamInfo src, ParamInfo dest) {
        return new InsertParamTransformer(src, dest);
    }

    @Override
    protected void applyPatch(String src) throws CannotCompileException {
        for (LineNumberAndPatchType patchLoc : locs) {
            applyPatch(src, patchLoc.lineNumber);
        }
    }

    private void applyPatch(String src, int loc) throws CannotCompileException {
        ctMethodToPatch.insertAt(loc, src);
    }

    public enum InsertPatchType {
        ABSOLUTE, RELATIVE
    }

    protected class InsertParamTransformer extends ParamTransformer {

        protected InsertParamTransformer(ParamInfo src, ParamInfo dest) {
            super(src, dest);
        }

        @Override
        protected String getParamName() throws PatchingException {
            int localVarStartPosition = destInfo.getParamCount() - info.localvars().length + 1;
            if (destInfo.getPosition() >= localVarStartPosition) {
                int idx = destInfo.getPosition() - localVarStartPosition;
                return info.localvars()[idx];
            }

            return super.getParamName();
        }
    }

    public static class LineNumberAndPatchType {
        public int lineNumber;
        public int relativeLineNumber;
        public InsertPatchType patchType;

        public LineNumberAndPatchType(int lineNumber) {
            this.lineNumber = lineNumber;
            this.patchType = InsertPatchType.ABSOLUTE;
        }

        public LineNumberAndPatchType(int lineNumber, int relativeLineNumber) {
            this.lineNumber = lineNumber;
            this.relativeLineNumber = relativeLineNumber;
            this.patchType = InsertPatchType.RELATIVE;
        }

    }
}
